package flock;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.util.Arrays;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import generics.AbstractGroup;
import generics.AbstractSprite;
import gui.Canvas;
import gui.ResourceStrings;

/**
 * Flock class: Extends the AbstractGroup class Creates a group of FlockMembers
 * that obey flocking behaviors, with various adjustable parameters.
 * 
 * @author Thomas Marmaduke
 * 
 */

public class Flock extends AbstractGroup {
	/**
	 * Parameters names that are to be assigned to the GUI to label the
	 * RangeSliders so that the user can change various parameters
	 */
	public static final String NUMBER = ResourceStrings
			.getString("FlockNumber");
	public static final String SEPARATION = ResourceStrings
			.getString("FlockSeparation");
	public static final String GROUP_SEPARATION = ResourceStrings
			.getString("FlockGroupSeparation");
	public static final String ALIGNMENT = ResourceStrings
			.getString("FlockAlignment");
	public static final String MAX_SPEED = ResourceStrings
			.getString("FlockMaxSpeed");
	public static final String WIDTH = ResourceStrings.getString("FlockWidth");
	public static final String HEIGHT = ResourceStrings
			.getString("FlockHeight");
	public static final String RED_CONTENT = ResourceStrings
			.getString("FlockRed");
	public static final String BLUE_CONTENT = ResourceStrings
			.getString("FlockBlue");
	public static final String GREEN_CONTENT = ResourceStrings
			.getString("FlockGreen");
	public static final String MOVER_TYPE = "Boid";
	public static final int CREATION_RANGE = 100;

	/**
	 * Statically creates a Map with the parameter name as the key and a list of
	 * the parameters for the associated value The list of parameters are
	 * ordered (minimum value, default value, maximum value)
	 */
	private static SortedMap<String, List<Integer>> INIT_PARAMETER_DATA = 
		new TreeMap<String, List<Integer>>();
	static {
		INIT_PARAMETER_DATA.put(NUMBER, Arrays.asList(1, 20, 100));
		INIT_PARAMETER_DATA.put(SEPARATION, Arrays.asList(0, 25, 100));
		INIT_PARAMETER_DATA.put(GROUP_SEPARATION, Arrays.asList(0, 100, 200));
		INIT_PARAMETER_DATA.put(ALIGNMENT, Arrays.asList(0, 50, 200));
		INIT_PARAMETER_DATA.put(MAX_SPEED, Arrays.asList(0, 8, 20));
		INIT_PARAMETER_DATA.put(WIDTH, Arrays.asList(1, 5, 10));
		INIT_PARAMETER_DATA.put(HEIGHT, Arrays.asList(1, 5, 10));
		INIT_PARAMETER_DATA.put(RED_CONTENT, Arrays.asList(0, 0, 255));
		INIT_PARAMETER_DATA.put(BLUE_CONTENT, Arrays.asList(0, 0, 255));
		INIT_PARAMETER_DATA.put(GREEN_CONTENT, Arrays.asList(0, 0, 255));
	}

	/**
	 * Constructs a Flock, given the size of the canvas and the index number of
	 * the Flock. Calls the constructor of the superclass, and then populates
	 * the group with FlockMembers.
	 * 
	 * @param d
	 * @param index
	 */
	public Flock(Dimension d, Integer index) {
		super(d, index, INIT_PARAMETER_DATA);
		populateFlock();
	}

	/**
	 * Adds FlockMembers until the number of FlockMemebers is equal to the
	 * default number desired
	 */
	private void populateFlock() {
		for (int i = 0; i < getParameterValue(NUMBER); i++) {
			addFlockMember();
		}
	}

	/**
	 * Calls the superclass to update the parameters. Then calls a method to add
	 * and remove FlockMembers as necessary. After that, it updates all
	 * pertinent members of the Group (as in all the "Boids")
	 */
	public void update(Canvas canvas) {
		super.updateParameter();
		updateFlockSize();
		for (AbstractSprite member : getMembers()) {
			updateMember((FlockMember) member, canvas);
		}
	}

	/**
	 * Checks the number of flocks with the slider value This method will be
	 * changed to support a more elegant way of passing slider info to groups.
	 * 
	 * @param flocknumber
	 * @param d
	 */
	private void updateFlockSize() {
		int flocknumber = getParameterValue(NUMBER);
		while (flocknumber != getMembers().size()) {
			if (flocknumber > getMembers().size())
				addFlockMember();
			if (flocknumber < getMembers().size())
				super.removeNewestMover();
		}
	}

	/**
	 * Creates a new flock member and adds it to this flock
	 */
	public void addFlockMember() {
		int maxspeed = getParameterValue(MAX_SPEED);
		Point flocklocation = new Point(CREATION_RANGE, CREATION_RANGE);
		if (getMembers().size() != 0) {
			flocklocation = getGroupCenter();
		}
		super.addMover(new FlockMember(
						this,
						new Point((int) (Math.random() * 2 * CREATION_RANGE
								- CREATION_RANGE + flocklocation.x),
								(int) (Math.random() * 2 * CREATION_RANGE
										- CREATION_RANGE + flocklocation.y)),
						new Dimension(getParameterValue(WIDTH),
								getParameterValue(HEIGHT)),
						new Point(
								(int) (Math.random() * 2 * maxspeed - maxspeed),
								(int) (Math.random() * 2 * maxspeed - maxspeed)),
						new Color(getParameterValue(RED_CONTENT),
								getParameterValue(GREEN_CONTENT),
								getParameterValue(BLUE_CONTENT)), maxspeed,
						MOVER_TYPE));
	}

	/**
	 * Updates a flock member's position with respect to the canvas
	 * it is being shown in
	 * @param member member to be updated
	 * @param canvas
	 */
	private void updateMember(FlockMember member, Canvas canvas) {
		int separation = getParameterValue(SEPARATION);
		int groupsep = getParameterValue(GROUP_SEPARATION);
		int alignment = getParameterValue(ALIGNMENT);
		Dimension size = new Dimension(getParameterValue(WIDTH),
				getParameterValue(HEIGHT));
		int maxspeed = getParameterValue(MAX_SPEED);
		Color bc = new Color(getParameterValue(RED_CONTENT),
				getParameterValue(GREEN_CONTENT),
				getParameterValue(BLUE_CONTENT));
		member.updateParameters(canvas.getGroups(), this, separation, groupsep, 
				alignment, size, maxspeed, bc);
		member.wrap(canvas.getSize());
		member.update(canvas);
	}

	@Override
	public String toString() {
		return getClass().getSimpleName() + " " + getIndex();
	}
}